#include "exportobject.h"
#include "exporterutilitydesc.h"
#include "resource.h"
#include "maxtypes.h"
#include "stdmat.h"
#include <shaders.h>
#include "modstack.h"
#include <iskin.h>
#include "plugapi.h"
#include "matrix3.h"
#include "max.h"

////////////////////////////////////////////////////////////////////////////////////////////////
// CExportMesh
////////////////////////////////////////////////////////////////////////////////////////////////


void CExportMesh::FillData( INode *N )
{
	if (!N) return;

	ExportObject::FillData( N );

	Object* Obj = N->EvalWorldState(0).obj;
	TriObject *triobj = (TriObject*)Obj->ConvertToType(0,Class_ID(TRIOBJ_CLASS_ID, 0));
	if ( !triobj ) return;

	Interval Time = GetCOREInterface()->GetAnimRange();
	TimeValue Step = GetTicksPerFrame();
	MtlBaseLib &ML = GetCOREInterface()->GetMaterialLibrary();

	Mtl* M = N->GetMtl();
	Mesh& mesh = triobj->GetMesh();

	Interface* ip = GetCOREInterface();

	string msg = std::string("Building normals %s") + string(N->GetName());
	ip->ProgressUpdate(0, FALSE, const_cast<char*>(msg.c_str()));

	mesh.buildNormals();

	ip->ProgressUpdate(99, FALSE, const_cast<char*>(msg.c_str()));

	NumFaces = mesh.numFaces;
	NumVertices = mesh.numVerts;
	NumCVertices = mesh.numCVerts;

	Name = N->GetName();

	// Evaluate surfaces (materials)
	bool bMaterial = M!=NULL;
	bool bSubMtrl;
	if ( bMaterial )
	{
		bSubMtrl = bMaterial?M->NumSubMtls()>0:false;
	}

	NumSurfaces = bMaterial?(bSubMtrl?M->NumSubMtls():1):1;

	Surfaces = new ExportSurface[NumSurfaces];

	msg = std::string("Building surfaces %s") + string(N->GetName());
	ip->ProgressUpdate(0, FALSE, const_cast<char*>(msg.c_str()));

	for ( int i = 0; i < NumSurfaces; i++ )
	{
		if ( !M )
		{
			Surfaces[i].Material = new ExportMaterial();
			Surfaces[i].Material->FillData( NULL );
		}
		else
		{
			Mtl* material = bSubMtrl?M->GetSubMtl(i):M;
			map<string,ExportMaterial*>::iterator it = ExporterUtility::SceneMaterials.find(string(material->GetName()));
			if (it == ExporterUtility::SceneMaterials.end())
			{
				Surfaces[i].Material = new ExportMaterial();
				Surfaces[i].Material->FillData( material );
				ExporterUtility::SceneMaterials[Surfaces[i].Material->Name] = Surfaces[i].Material;
			} else {
				Surfaces[i].Material = it->second;
			}
			
			Surfaces[i].Material->FillData( material );
		}

		ip->ProgressUpdate(i/float(NumSurfaces)*100, FALSE, const_cast<char*>(msg.c_str()));
	}

	msg = std::string("Building faces %s") + string(N->GetName());
	ip->ProgressUpdate( 0, FALSE, const_cast<char*>(msg.c_str()) );

	Vertices = new ExportVertex[NumVertices];

	for (int i = 0; i < NumVertices; i++)
	{
		Vertices[i].Vertex = mesh.verts[i];
		Vertices[i].id = i;
	}

	if ( NumCVertices )
	{
		Colors = new ExportColor[NumCVertices];
	}

	for (int i = 0; i < NumCVertices; i++)
	{
		Colors[i].Color.r = mesh.vertCol[i].x;
		Colors[i].Color.g = mesh.vertCol[i].y;
		Colors[i].Color.b = mesh.vertCol[i].z;
	}

	Faces = new ExportFace[NumFaces];

	int NumMaps = mesh.getNumMaps();
	for (int i = 0; i < NumFaces; i++ )
	{
		Face *F = mesh.faces+i;
		//TVFace *CF = &mesh.vcFace[i]; // Color faces
		//Mtl* material = bMaterial?(bSubMtrl?M->GetSubMtl(F->getMatID()):M):NULL;
		Faces[i].vertex[0] = F->v[0];
		Faces[i].vertex[1] = F->v[1];
		Faces[i].vertex[2] = F->v[2];
		Faces[i].SmGroup = F->smGroup;
		Faces[i].id = i;
		if ( NumCVertices )
		{
			Faces[i].color[0] = mesh.vcFace[i].t[0];
			Faces[i].color[1] = mesh.vcFace[i].t[1];
			Faces[i].color[2] = mesh.vcFace[i].t[2];
		}
		Faces[i].Normal = mesh.getFaceNormal(i);
		Faces[i].CleanUpData();
#if 0
		if ( mesh.tVerts )
		{
			for ( int map = 0; map < m_iUVChannels; map++ )
			{
				TVFace *MapFaces = mesh.mapFaces(map+1);
				UVVert *MapVertices = mesh.mapVerts(map+1);
				TVFace *TF = MapFaces + i; // Texture faces
				UVVert TV = MapVertices[TF->t[j]];
				EF->Vertices[j].UV[map].x = TV.x;
				EF->Vertices[j].UV[map].y = 1.f-TV.y;
			}
		}
		if ( mesh.vertCol )
		{
			Point3 VC = mesh.vertCol[CF->t[j]];
			EF->Vertices[j].Diffuse.r = VC.x;
			EF->Vertices[j].Diffuse.g = VC.y;
			EF->Vertices[j].Diffuse.b = VC.z;
		}
		UVVert* v = mesh.mapVerts(MAP_ALPHA);
		//if ( v ) EF->Vertices[j].Diffuse.a = v[F->v[j]].x;
		if ( material )
		{
			Point3 S =material->GetSpecular();
			EF->Vertices[j].Specular.r = S.x;
			EF->Vertices[j].Specular.g = S.y;
			EF->Vertices[j].Specular.b = S.z;
			//EF->Vertices[j].Diffuse.a *= 1.f-(material->GetXParency());
		} else {
			EF->Vertices[j].Specular = Color(0.f,0.f,0.f);
		}
#endif
		//ExportSurfaces[(bMaterial&&bSubMtrl)?mesh.faces[i].getMatID():0].Faces.Add(EF);
		int SurfaceIndex = (bMaterial&&bSubMtrl)?mesh.faces[i].getMatID():0;
		if ( SurfaceIndex > NumSurfaces-1 ) SurfaceIndex = NumSurfaces-1;
		//int FaceCount = Surfaces[SurfaceIndex].Faces.size();
		Surfaces[SurfaceIndex].Faces.push_back(i);

		if ( !(i%50) )
		{
			ip->ProgressUpdate(i/float(NumFaces)*100, FALSE, const_cast<char*>(msg.c_str()));
		}
	}

	int j = 0, l = 0;

	// Export children
	if ( !bNoChildren )
		for ( int i = 0; i < N->NumberOfChildren(); i++ )
		{
			ExportObject *O = ExportObject::BuildFromINode(N->GetChildNode(i));
			if ( O ) Children.push_back( O );
		}

#if 0
		// Optimize
		if ( IsDlgButtonChecked( ExporterUtilityDesc::ClassInstance()->hRollup, IDC_OPTIMIZE ) && !bNoOptimize )
		{
			sprintf ( tmpstr, "Optimizing %s", N->GetName() );
			ip->ProgressUpdate( 0, FALSE, tmpstr );
			Optimize();
		}

		// Vertex anim
		if ( bVertexAnim ) NumVertAnimPhases = Time.Duration()/Step+(Time.Duration()>1?1:0);
		if ( NumVertAnimPhases > 1 )
		{
			sprintf ( tmpstr, "Building vertex anim %s", N->GetName() );
			ip->ProgressUpdate( 0, FALSE, tmpstr );
			Vertices = (ExportVertex*)realloc( Vertices, sizeof(ExportVertex)*NumVertices*NumVertAnimPhases );

			int phase = 1;
			// Start from second phase
			for ( TimeValue t = Time.Start()+Step; t <= Time.End(); t += Step )
			{
				Object* Obj = N->EvalWorldState(t).obj;
				TriObject *triobj = (TriObject*)Obj->ConvertToType(t,Class_ID(TRIOBJ_CLASS_ID, 0));
				//ASSERT(triobj);
				Mesh& mesh = triobj->GetMesh();
				bool *bVerticesUpd = new bool[NumVertices];
				ZeroMemory( bVerticesUpd, sizeof(bool)*NumVertices );
				for ( int i = 0; i < NumFaces; i++ )
					for ( int j = 0; j < 3; j++ )
					{
						int origvertindex = mesh.faces[Faces[i].OrigIndex].getVert(2-j);
						Vertices[NumVertices*phase+Faces[i].i[j]].Vertex.x = mesh.verts[origvertindex].x;
						Vertices[NumVertices*phase+Faces[i].i[j]].Vertex.y = mesh.verts[origvertindex].y;
						Vertices[NumVertices*phase+Faces[i].i[j]].Vertex.z = mesh.verts[origvertindex].z;
						bVerticesUpd[Faces[i].i[j]] = true;
					}
					phase++;
					for ( i = 0; i < NumVertices; i++ )
					{
						//	ASSERT(bVerticesUpd[i]);
					}
					delete[] bVerticesUpd;
			}
		}
#endif


		//DPrint("Created Geometry from %s\n", N->GetName() );
}

void CExportMesh::WriteSurface(ostream &stream, ExportSurface &surf)
{
	stream << "<Surface Material=\"" << surf.Material->Name << "\">" << endl;
	stream << "\t<Faces>" << endl;
	for (vector<int>::const_iterator it = surf.Faces.begin(); it != surf.Faces.end(); ++it)
	{
		Faces[*it].Write(stream, HasVertexColors);		
	}
	stream << "\t</Faces>" << endl;
	stream << "</Surface>" << endl;
}

void CExportMesh::BeginWrite( ostream &stream )
{
	int i = 0;
	stream << "<Mesh Name=\"" << Name << "\" Type=\"" + Type << "\">" << endl;

	stream << "<Transformation>";

	for (int i = 0; i < 3; i++)
	{
		stream << Transformation.GetColumn3(i).x << " " << Transformation.GetColumn3(i).y << " " << Transformation.GetColumn3(i).z << (i==3?" 1":" 0");
		stream << " ";
	}

	stream << Transformation.GetTrans().x << " " << Transformation.GetTrans().y << " " << Transformation.GetTrans().z << " 1";

	stream << "</Transformation>" << endl;

	stream << "<Vertices>" << endl;

	for ( i = 0; i < NumVertices; i++ )
	{
		Vertices[i].Write(stream);
	}
	stream << "</Vertices>" << endl;

	stream << "<Surfaces>" << endl;

	for (int i = 0; i < NumSurfaces; i++)
	{
		WriteSurface(stream, Surfaces[i]);

	}

	stream << "</Surfaces>" << endl;

	/*
	if ( NumVertAnimPhases > 1 )
	for ( i = 0; i < NumVertices*(NumVertAnimPhases-1); i++ )
	{
	_FWRITE( F, Vertices[NumVertices+i].Vertex );
	_FWRITE( F, Vertices[NumVertices+i].Normal );
	}

	for ( i = 0; i < NumFaces; i++ )
	{
	F.Write( Faces[i].i, sizeof(WORD)*dim_of(Faces[i].i) );
	}

	_FWRITE( F, NumUsedSurfaces );
	for ( i = 0; i < NumSurfaces; i++ )
	{
	if ( Surfaces[i].FaceCount )
	{
	_FWRITE( F, Surfaces[i].StartFace );
	_FWRITE( F, Surfaces[i].FaceCount );
	Surfaces[i].Material.Save( F );
	}
	}
	*/


}

void CExportMesh::EndWrite( ostream &stream )
{
	stream << "</Mesh>" << endl;
}

CExportMesh::~CExportMesh()
{
	delete[] Vertices;
	delete[] Faces;
	delete[] Surfaces;
	delete[] Colors;
}